/*
 * Copyright (c) 2012 Kent Ryhorchuk
 *
 * Use of this source code is governed by a MIT-style
 * license that can be found in the LICENSE file or at
 * https://opensource.org/licenses/MIT
 */
#include <stdarg.h>
#include <lk/reg.h>
#include <lk/debug.h>
#include <stdio.h>
#include <assert.h>
#include <lib/cbuf.h>
#include <kernel/thread.h>
#include <platform/debug.h>
#include <arch/ops.h>
#include <dev/uart.h>
#include <target/debugconfig.h>
#include <hc32_ddl.h>
#include <arch/arm/cm.h>
#include <hc32f46x.h>

#ifdef ENABLE_UART1
cbuf_t uart1_rx_buf;
#ifndef UART1_FLOWCONTROL
#define UART1_FLOWCONTROL UsartRtsEnable
#endif
#ifndef UART1_BAUDRATE
#define UART1_BAUDRATE 115200
#endif
#ifndef UART1_RXBUF_SIZE
#define UART1_RXBUF_SIZE 64
#endif
#endif

#ifdef ENABLE_UART2
cbuf_t uart2_rx_buf;
#ifndef UART2_FLOWCONTROL
#define UART2_FLOWCONTROL UsartRtsEnable
#endif
#ifndef UART2_BAUDRATE
#define UART2_BAUDRATE 115200
#endif
#ifndef UART2_RXBUF_SIZE
#define UART2_RXBUF_SIZE 64
#endif
#endif

#ifdef ENABLE_UART3
cbuf_t uart3_rx_buf;
#ifndef UART3_FLOWCONTROL
#define UART3_FLOWCONTROL UsartRtsEnable
#endif
#ifndef UART3_BAUDRATE
#define UART3_BAUDRATE 115200
#endif
#ifndef UART3_RXBUF_SIZE
#define UART3_RXBUF_SIZE 64
#endif
#endif

#ifdef ENABLE_UART4
cbuf_t uart6_rx_buf;
#ifndef UART4_FLOWCONTROL
#define UART4_FLOWCONTROL UsartRtsEnable
#endif
#ifndef UART4_BAUDRATE
#define UART4_BAUDRATE 115200
#endif
#ifndef UART4_RXBUF_SIZE
#define UART4_RXBUF_SIZE 64
#endif
#endif

#ifdef ENABLE_UART1
static void Usart1RxIrqCallback(void);
static void Usart1ErrIrqCallback(void);
#endif

#ifdef ENABLE_UART2
static void Usart2RxIrqCallback(void);
static void Usart2ErrIrqCallback(void);
#endif

#ifdef ENABLE_UART3
static void Usart3RxIrqCallback(void);
static void Usart3ErrIrqCallback(void);
#endif

#ifdef ENABLE_UART4
static void Usart4RxIrqCallback(void);
static void Usart5ErrIrqCallback(void);
#endif

static void usart_init1_early(M4_USART_TypeDef *usart, uint32_t baud, uint16_t flowcontrol) {
    stc_usart_uart_init_t init;

    init.enDataLength = UsartDataBits8;
    init.enStopBit = UsartOneStopBit;
    init.enParity = UsartParityNone;
    init.enHwFlow = flowcontrol;
	
	init.enClkMode = UsartIntClkCkNoOutput;
	init.enClkDiv = UsartClkDiv_16;
	init.enDirection = UsartDataLsbFirst;
	init.enSampleMode = UsartSamleBit8;
	init.enDetectMode = UsartStartBitFallEdge;	

	USART_UART_Init(usart, &init);
	USART_SetBaudrate(usart, baud);
    /*Enable RX && TX function*/
    USART_FuncCmd(usart, UsartRx, Enable);
    USART_FuncCmd(usart, UsartTx, Enable);
}


void uart_init_early(void) {
#ifdef ENABLE_UART1
    PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART1, Enable);
    usart_init1_early(M4_USART1, UART1_BAUDRATE, UART1_FLOWCONTROL);
#endif
#ifdef ENABLE_UART2
    PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART2, Enable);
    usart_init1_early(M4_USART2, UART2_BAUDRATE, UART2_FLOWCONTROL);
#endif
#ifdef ENABLE_UART3
    PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART3, Enable);
    usart_init1_early(M4_USART3, UART3_BAUDRATE, UART3_FLOWCONTROL);
#endif
#ifdef ENABLE_UART4
    PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART4, Enable);
    usart_init1_early(M4_USART4, UART4_BAUDRATE, UART4_FLOWCONTROL);
#endif
}

void uart_init(void) {
	stc_irq_regi_conf_t stcIrqRegiCfg;
#ifdef ENABLE_UART1
	cbuf_initialize(&uart1_rx_buf, UART1_RXBUF_SIZE);
	/* Set USART RX IRQ */      
	stcIrqRegiCfg.enIRQn = Int000_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART1_RI;
	stcIrqRegiCfg.pfnCallback = &Usart1RxIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

	/* Set USART RX error IRQ */
	stcIrqRegiCfg.enIRQn = Int001_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART1_EI;
	stcIrqRegiCfg.pfnCallback = &Usart1ErrIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

	/*Enable TX RX && RX interupt function*/
	USART_FuncCmd(M4_USART1, UsartRx, Enable);
	USART_FuncCmd(M4_USART1, UsartTx, Enable);
	USART_FuncCmd(M4_USART1, UsartRxInt, Enable);    
#endif
#ifdef ENABLE_UART2
    cbuf_initialize(&uart2_rx_buf, UART2_RXBUF_SIZE);
	/* Set USART RX IRQ */      
	stcIrqRegiCfg.enIRQn = Int004_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART2_RI;
	stcIrqRegiCfg.pfnCallback = &Usart2RxIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

	/* Set USART RX error IRQ */
	stcIrqRegiCfg.enIRQn = Int005_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART2_EI;
	stcIrqRegiCfg.pfnCallback = &Usart2ErrIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);


	/*Enable RX && RX interupt function*/
	USART_FuncCmd(M4_USART2, UsartRx, Enable);
	USART_FuncCmd(M4_USART2, UsartTx, Enable);
	USART_FuncCmd(M4_USART2, UsartRxInt, Enable);	
#endif
#ifdef ENABLE_UART3
    cbuf_initialize(&uart3_rx_buf, UART3_RXBUF_SIZE);
	USART_UART_Init(M4_USART3, (stc_usart_uart_init_t*)(obj->privdata));
	USART_SetBaudrate(M4_USART3, baudrate);

	/* Set USART RX IRQ */      
	stcIrqRegiCfg.enIRQn = Int008_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART3_RI;
	stcIrqRegiCfg.pfnCallback = &Usart3RxIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

	/* Set USART RX error IRQ */
	stcIrqRegiCfg.enIRQn = Int009_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART3_EI;
	stcIrqRegiCfg.pfnCallback = &Usart3ErrIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

	/*Enable RX && RX interupt function*/
	USART_FuncCmd(M4_USART3, UsartRx, Enable);
	USART_FuncCmd(M4_USART3, UsartTx, Enable);
	USART_FuncCmd(M4_USART3, UsartRxInt, Enable);	
	
#endif
#ifdef ENABLE_UART4
    cbuf_initialize(&uart4_rx_buf, UART4_RXBUF_SIZE);
	USART_UART_Init(M4_USART4, (stc_usart_uart_init_t*)(obj->privdata));
	USART_SetBaudrate(M4_USART4, baudrate);

	/* Set USART RX IRQ */      
	stcIrqRegiCfg.enIRQn = Int012_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART4_RI;
	stcIrqRegiCfg.pfnCallback = &Usart4RxIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

	/* Set USART RX error IRQ */
	stcIrqRegiCfg.enIRQn = Int013_IRQn;
	stcIrqRegiCfg.enIntSrc = INT_USART4_EI;
	stcIrqRegiCfg.pfnCallback = &Usart4ErrIrqCallback;
	enIrqRegistration(&stcIrqRegiCfg);
	NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
	NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
	NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);


	/*Enable RX && RX interupt function*/
	USART_FuncCmd(M4_USART4, UsartRx, Enable);
	USART_FuncCmd(M4_USART4, UsartTx, Enable);
	USART_FuncCmd(M4_USART4, UsartRxInt, Enable);	
#endif
}

void uart_rx_irq(M4_USART_TypeDef *usart, cbuf_t *rxbuf) {
    arm_cm_irq_entry();

    bool resched = false;
    while (Set == USART_GetStatus(usart, UsartRxNoEmpty)) {
        if (!cbuf_space_avail(rxbuf)) {
            // Overflow - let flow control do its thing by not
            // reading the from the FIFO.
			USART_FuncCmd(usart, UsartRxInt, Disable);
            break;
        }

        char c = USART_RecData(usart);
        cbuf_write_char(rxbuf, c, false);
        resched = true;
    }

    arm_cm_irq_exit(resched);
}

#ifdef ENABLE_UART1
static void Usart1RxIrqCallback(void)
{
	uart_rx_irq(M4_USART1, &uart1_rx_buf);
}

static void Usart1ErrIrqCallback(void)
{
  if (Set == USART_GetStatus(M4_USART1, UsartFrameErr)){
    USART_ClearStatus(M4_USART1, UsartFrameErr);
  }

  if (Set == USART_GetStatus(M4_USART1, UsartParityErr)){
    USART_ClearStatus(M4_USART1, UsartParityErr);
  }

  if (Set == USART_GetStatus(M4_USART1, UsartOverrunErr)){
    USART_ClearStatus(M4_USART1, UsartOverrunErr);
  }
}
#endif

#ifdef ENABLE_UART2
static void Usart2RxIrqCallback(void)
{
	uart_rx_irq(M4_USART2, &uart2_rx_buf);
}

static void Usart2ErrIrqCallback(void)
{
  if (Set == USART_GetStatus(M4_USART2, UsartFrameErr)){
    USART_ClearStatus(M4_USART2, UsartFrameErr);
  }

  if (Set == USART_GetStatus(M4_USART2, UsartParityErr)){
    USART_ClearStatus(M4_USART2, UsartParityErr);
  }

  if (Set == USART_GetStatus(M4_USART2, UsartOverrunErr)){
    USART_ClearStatus(M4_USART2, UsartOverrunErr);
  }
}
#endif

#ifdef ENABLE_UART3
static void Usart3RxIrqCallback(void)
{
	uart_rx_irq(M4_USART3, &uart3_rx_buf);
}

static void Usart3ErrIrqCallback(void)
{
  if (Set == USART_GetStatus(M4_USART3, UsartFrameErr)){
    USART_ClearStatus(M4_USART3, UsartFrameErr);
  }

  if (Set == USART_GetStatus(M4_USART3, UsartParityErr)){
    USART_ClearStatus(M4_USART3, UsartParityErr);
  }

  if (Set == USART_GetStatus(M4_USART3, UsartOverrunErr)){
    USART_ClearStatus(M4_USART3, UsartOverrunErr);
  }
}
#endif

#ifdef ENABLE_UART4
static void Usart4RxIrqCallback(void)
{
	uart_rx_irq(M4_USART4, &uart4_rx_buf);
}

static void Usart4ErrIrqCallback(void)
{
  if (Set == USART_GetStatus(M4_USART4, UsartFrameErr)){
    USART_ClearStatus(M4_USART4, UsartFrameErr);
  }

  if (Set == USART_GetStatus(M4_USART4, UsartParityErr)){
    USART_ClearStatus(M4_USART4, UsartParityErr);
  }

  if (Set == USART_GetStatus(M4_USART4, UsartOverrunErr)){
    USART_ClearStatus(M4_USART4, UsartOverrunErr);
  }
}
#endif

static void usart_putc(M4_USART_TypeDef *usart, char c) {
    while (USART_GetStatus(usart, UsartTxEmpty) == Reset);
    USART_SendData(usart, c);
    //while (USART_GetStatus(usart, UsartTxComplete) == Reset);
}

static int usart_getc(M4_USART_TypeDef *usart, cbuf_t *rxbuf, bool wait) {
    unsigned char c;
    if (cbuf_read_char(rxbuf, (char *) &c, wait) == 0)
        return -1;
    if (cbuf_space_avail(rxbuf) > cbuf_size(rxbuf))
		USART_FuncCmd(usart, UsartRxInt, Enable);

    return c;
}

static M4_USART_TypeDef *get_usart(int port) {
    switch (port) {
#ifdef ENABLE_UART1
        case 1:
            return M4_USART1;
#endif
#ifdef ENABLE_UART2
        case 2:
            return M4_USART2;
#endif
#ifdef ENABLE_UART3
        case 3:
            return M4_USART3;
#endif
#ifdef ENABLE_UART4
        case 6:
            return M4_USART4;
#endif
        default:
            ASSERT(false);
            return 0;
    }
}

static cbuf_t *get_rxbuf(int port) {
    switch (port) {
#ifdef ENABLE_UART1
        case 1:
            return &uart1_rx_buf;
#endif
#ifdef ENABLE_UART2
        case 2:
            return &uart2_rx_buf;
#endif
#ifdef ENABLE_UART3
        case 3:
            return &uart3_rx_buf;
#endif
#ifdef ENABLE_UART6
        case 6:
            return &uart6_rx_buf;
#endif
        default:
            ASSERT(false);
            return 0;
    }
}

int uart_putc(int port, char c) {
    M4_USART_TypeDef *usart = get_usart(port);
    usart_putc(usart, c);
    return 1;
}

int uart_getc(int port, bool wait) {
    cbuf_t *rxbuf = get_rxbuf(port);
    M4_USART_TypeDef *usart = get_usart(port);

    return usart_getc(usart, rxbuf, wait);
}

void uart_flush_tx(int port) {}

void uart_flush_rx(int port) {}

void uart_init_port(int port, uint baud) {
    M4_USART_TypeDef *usart = get_usart(port);

	USART_SetBaudrate(usart, baud);
}

// inject a character into the console uart rx buffer
void __debugger_console_putc(char c) {
    cbuf_t *rxbuf = get_rxbuf(DEBUG_UART);
    if (rxbuf && cbuf_space_avail(rxbuf)) {
        cbuf_write_char(rxbuf, c, false);
    }
}

